package com.xlh.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.cell.CellUtil;
import com.xlh.annotation.ExcelColumnAnnotation;
import com.xlh.exception.common.GlobalException;
import com.xlh.service.ExcelService;
import com.xlh.util.ExcelParseUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;

import static com.xlh.util.CellStyleUtils.*;

/**
 * @author zhr 2019/3/1
 */
@Service
public class ExcelServiceImpl implements ExcelService {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExcelServiceImpl.class);

    @Override
    public void downloadExcelModel(HttpServletResponse res, HttpServletRequest req, String fileName, String modelPosition) throws IOException {
        res.setContentType("multipart/form-data");
        res.setHeader("Content-Disposition", "inline; fileName=" + fileName + ";filename*=utf-8''" +
                URLEncoder.encode(fileName, "UTF-8"));
        HSSFWorkbook wb = new HSSFWorkbook(new ClassPathResource(modelPosition).getInputStream());

        try (OutputStream fileOut = res.getOutputStream()) {
            wb.write(fileOut);
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }

    @Override
    public void downloadExcelData(HttpServletResponse res, HttpServletRequest req, String fileName,
                                  String modelPosition, List<List<String>> data, int rowNum) throws IOException {
        res.setContentType("multipart/form-data");
        res.setHeader("Content-Disposition", "inline; fileName=" + fileName +
                ";filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));
        HSSFWorkbook wb = new HSSFWorkbook(new ClassPathResource(modelPosition).getInputStream());
        if (CollectionUtils.isNotEmpty(data)) {
            Sheet sheet = wb.getSheetAt(0);
            for (int i = 0; i < data.size() ; i++) {
                Row row = sheet.createRow(i + rowNum);
                for (int index = 0; index < data.get(i).size(); index++) {
                    row.createCell(index).setCellValue(data.get(i).get(index));
                }
            }
        }

        try (OutputStream fileOut = res.getOutputStream()) {
            wb.write(fileOut);
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }



    @Override
    public void downloadExcelData(HttpServletResponse res, String fileName, List<List<String>> data, int rowNum) throws IOException {
        res.setContentType("multipart/form-data");
        res.setHeader("Content-Disposition", "inline; fileName=" + fileName +
                ";filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));
        HSSFWorkbook wb = new HSSFWorkbook();

        Sheet sheet = wb.createSheet("学生信息导出");
        // 1. 设置头背景色
        HSSFCellStyle cellStyle = wb.createCellStyle();
        addFullBorder(cellStyle);
        CellStyle headerStyle = universalTitleStyle(wb);
        CellStyle secondHeaderStyle = creadeSecondHeaderStyle(wb);

        String[] title = {"姓名", "学号","性别","手机号", "邮箱"};
        Integer lastIndex = title.length - 1;

        for (int i = 0; i <= 1; i++) {

            if(i == 0 ){
                sheet.createRow(i).setHeight((short)800);
                for (int j = 0; j <= lastIndex; j++) {
                    sheet.createRow(i).createCell(j).setCellStyle(headerStyle);
                }
                // 合并
                CellUtil.mergingCells(sheet, 0, 0, 0, lastIndex, headerStyle);
                sheet.getRow(0).setHeight((short)800);
                sheet.getRow(0).getCell(0).setCellStyle(headerStyle);
                sheet.getRow(0).getCell(0).setCellValue("班级管理-学生数据清单");
            }else{
                sheet.createRow(i).setHeight((short)400);
                for (int j = 0; j <= lastIndex; j++) {
                    Cell cell = sheet.getRow(i).createCell(j);
                    cell.setCellStyle(secondHeaderStyle);
                    cell.setCellValue(title[j]);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(data)) {

            for (int i = 0; i < data.size() ; i++) {
                Row row = sheet.createRow(i + rowNum);
                for (int index = 0; index < data.get(i).size(); index++) {
                    row.createCell(index).setCellValue(data.get(i).get(index));
                    row.getCell(index).setCellStyle(cellStyle);
                }
            }
        }

        // 设置列宽度
        for (int i = 0; i < title.length; i++) {
            sheet.setColumnWidth(i, 20*256);
        }

        try (OutputStream fileOut = res.getOutputStream()) {
            wb.write(fileOut);
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
        }
    }



    
    @Override
    public void downloadExcelModel(HttpServletResponse res, String fileName, String fileDir,
                                   int row, List<List<String>> rowData) {

        res.setContentType("application/vnd.ms-excel;charset=utf-8");
        //test.xls是弹出下载对话框的文件名，不能为中文，中文请自行编码
        try {
            res.setHeader("Content-Disposition", "attachment;filename=" + fileName +
                    ";filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e.getMessage());
        }

        ExcelWriter writer = null;
        ServletOutputStream out = null;

        try {

            writer = ExcelUtil.getWriter(fileDir);
            writer.setDestFile(null);
            if (CollectionUtils.isNotEmpty(rowData)) {
                writer.passRows(row);
                writer.write(rowData);
            }

            out = res.getOutputStream();
            writer.flush(out);

        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
            if (out != null) {
                IoUtil.close(out);
            }
        }

    }

    @Override
    public <T> List<T> parseFile(MultipartFile file, Class<T> clazz, int startLine, int sheetIndex) {
        String extension = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        if (!isExcel(extension)) throw new GlobalException("请上传Excel文件");

        try {
            List<T> result = ExcelParseUtils.readAll(file, startLine, sheetIndex, clazz);
            if (CollectionUtils.isNotEmpty(result)) {
                T firstRow = result.get(0);
                if (!isLegal(firstRow, clazz)) {
                    throw new GlobalException("Excel模板类型不正确,请上传正确的Excel文件");
                } else {
                    result.remove(0);
                }
            }
            return result;
        } catch (Exception e) {
            throw new GlobalException(e.getMessage());
        }
    }

    private boolean isExcel(String extension) {
        return StringUtils.equals(extension, ".xls") || StringUtils.equals(extension, ".xlsx");
    }

    private <T> boolean isLegal(T firstRow, Class<T> clazz) {
        boolean b = true;
        if (clazz.getSuperclass() != null) {
            b = isLegal(firstRow, clazz.getSuperclass());
        }
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            String fieldName = field.getAnnotation(ExcelColumnAnnotation.class).value();
            String fieldValue = getValue(field, firstRow);

            b = b && Objects.equals(fieldName, fieldValue);
        }
        return b;
    }

    @SuppressWarnings("unchecked")
    private <T> String getValue(Field field, T firstRow) {
        //获取属性的名字
        String name = field.getName();
        //将属性名字的首字母大写
        name = name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());

        Class clazz = firstRow.getClass();

        try {
            //整合出 getId() 属性这个方法
            Method m = clazz.getMethod(String.format("get%s", name));

            return (String) m.invoke(firstRow);
        } catch (Exception e) {
            return null;
        }
    }
}
